/*
 *                               POK header
 *
 * The following file is a part of the POK project. Any modification should
 * be made according to the POK licence. You CANNOT use this file or a part
 * of a file for your own project.
 *
 * For more information on the POK licence, please see our LICENCE FILE
 *
 * Please follow the coding guidelines described in doc/CODING_GUIDELINES
 *
 *                                      Copyright (c) 2007-2021 POK team
 */

/* s_scalbnf.c -- float version of s_scalbn.c.
 * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com.
 */

/*
 * ====================================================
 * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
 *
 * Developed at SunPro, a Sun Microsystems, Inc. business.
 * Permission to use, copy, modify, and distribute this
 * software is freely granted, provided that this notice
 * is preserved.
 * ====================================================
 */

#ifdef POK_NEEDS_LIBMATH

#include "math_private.h"
#include <libm.h>

static const float two25 = 3.355443200e+07, /* 0x4c000000 */
    twom25 = 2.9802322388e-08,              /* 0x33000000 */
    huge = 1.0e+30, tiny = 1.0e-30;

float scalbnf(float x, int n) {
  int32_t k, ix;
  GET_FLOAT_WORD(ix, x);
  k = (ix & 0x7f800000) >> 23; /* extract exponent */
  if (k == 0) {                /* 0 or subnormal x */
    if ((ix & 0x7fffffff) == 0)
      return x; /* +-0 */
    x *= two25;
    GET_FLOAT_WORD(ix, x);
    k = ((ix & 0x7f800000) >> 23) - 25;
    if (n < -50000)
      return tiny * x; /*underflow*/
  }
  if (k == 0xff)
    return x + x; /* NaN or Inf */
  k = k + n;
  if (k > 0xfe)
    return huge * copysignf(huge, x); /* overflow  */
  if (k > 0)                          /* normal result */
  {
    SET_FLOAT_WORD(x, (ix & 0x807fffff) | (k << 23));
    return x;
  }
  if (k <= -25) {
    if (n > 50000)                      /* in case integer overflow in n+k */
      return huge * copysignf(huge, x); /*overflow*/
    else
      return tiny * copysignf(tiny, x); /*underflow*/
  }
  k += 25; /* subnormal result */
  SET_FLOAT_WORD(x, (ix & 0x807fffff) | (k << 23));
  return x * twom25;
}

#endif
